﻿package net
{
	import flash.events.*;
	import flash.net.*;
	import flash.utils.*;
	
	/**
	* ...
	* @author cuiwei
	*/
	public class DSocket extends EventDispatcher implements IDBufNetwork
	{
		private static const CONNECT_TIMEOUT:int = 50000;				//连接超时时间
		private static const RECONNECT_TIMES_IF_FAILD:int = 3;			//重试次数
		
		public var connected:Boolean = false;
		private var m_host:String;
		private var m_port:int;
		
		private var m_nStatus:int = 0;
		private var m_ndx:int = 0;
		private var m_packet:ByteArray;
		private var m_packetlen:uint = 0;
		private var m_readpacket:ByteArray;
		private var m_packetbody:ByteArray;
		private var m_readbodypos:int = 8;
		private var m_nreadPos:int = 0;
		private var m_nBodyLen:uint = 0;
		private var m_socket:Socket;
		
		private var m_proxyHost:String = ""; 				//代理服务器 ip
		private var m_proxyPort:int = 0; 					//代理服务器 port
		private var m_isProxyEnabled:Boolean = false; 		//备用代理服务器是否启用
		
		private const PACKET_HEADER_SIZE:int = 8;
		private const PACKET_BUFFER_SIZE:int = 16384;

		private const REQ_REQUEST:int = 0;
		private const REQ_HEADER:int = 1;
		private const REQ_BODY:int = 2;
		private const REQ_DONE:int = 3;
		private const REQ_PROCESS:int = 4;
		private const REQ_ERROR:int = 5;
		
		
		public function DSocket()
		{
			initVars();
		}
		
		public function setBackupProxy(host:String, port:int, is_primary:Boolean = false):void
		{
			trace("////proxy:" + host + ":" + port);
			this.m_proxyHost = host;
			this.m_proxyPort = port;
			m_isProxyEnabled = is_primary;		//是否直接启用代理服务器
		}
		
		private function initVars():void
		{
			m_packetlen = 0;
			m_nStatus = 0;
			m_nreadPos = 0;
			m_nBodyLen = 0;
			m_ndx = 0;
			connected = false;
			m_readbodypos = PACKET_HEADER_SIZE;
			m_packet = new ByteArray();
			m_packetbody = new ByteArray();
			m_readpacket = new ByteArray();		
			m_readpacket.endian = Endian.LITTLE_ENDIAN;
			m_packet.endian = Endian.LITTLE_ENDIAN;
			m_packetbody.endian = Endian.LITTLE_ENDIAN;			
		}

		public function writeBegin() : void
		{
			m_packetlen = 0;
			m_packet.position = 0;
			m_packet.length = 0;
			m_packetbody.position = 0;
			m_packetbody.length = 0;
			m_packet.writeInt(0x595a4448);
			return;
		}
		
		//发送特殊指令头,表示此消息是通过GameServer发给GameCenter的
		public function writeBeginToGameCenter():void
		{
			writeBegin();
			writeString("SCTC");
		}
		
		public function writeByte(nValue:int) : void
		{
			m_packetlen = m_packetlen + 1;
			m_packetbody.writeByte(nValue);
			return;
		}

		public function writeInt(nValue:int) : void
		{
			m_packetlen = m_packetlen + 4;
			m_packetbody.writeInt(nValue);
			return;
		}

		public function getbodylen() : int
		{
			m_readpacket.position = 4;
			return m_readpacket.readInt();
		}

		public function readUint() : uint
		{
			m_readpacket.position = m_readbodypos;
			m_readbodypos = m_readbodypos + 4;
			return m_readpacket.readUnsignedInt();
		}

		private function parse_header() : Boolean
		{
			var data:ByteArray;
			var _loc_2:int;
			data = new ByteArray();
			data.writeMultiByte("HDZY", "gb2132");
			if (m_readpacket[0] != data[0] || m_readpacket[1] != data[1] || m_readpacket[2] != data[2] || m_readpacket[3] != data[3])
			{
				data = null;
				return false;
			}
			data = null;
			_loc_2 = getcmd();
			if (_loc_2 <= 0 || _loc_2 >= 32000)
			{
				return false;
			}
			m_nBodyLen = getbodylen();
			if (m_nBodyLen >= 0 && m_nBodyLen < PACKET_BUFFER_SIZE - PACKET_HEADER_SIZE)
			{
				return true;
			}
			return false;
		}

		public function getcmd() : int
		{
			m_readpacket.position = 4;
			return m_readpacket.readShort();
		}

		private function socketDataFun(param1:ProgressEvent) : void
		{
			var len:int;
			var _loc_3:DSocketEvent;
			m_ndx = 0;
			len = m_socket.bytesAvailable;
			while (m_ndx < len && m_nStatus != REQ_ERROR)
			{
				// label
				switch(m_nStatus)
				{
					case REQ_REQUEST:
					case REQ_HEADER:
					{
						if (!read_header(len))
						{
							break;
						}
						if (!parse_header())
						{
							m_nStatus = REQ_ERROR;
							break;
						}
						else
						{
							m_nStatus = REQ_BODY;
						}// end case
					}// end else if
					case REQ_BODY:
					{
						if (parse_body(len))
						{
							m_nStatus = REQ_DONE;
						}
						break;
					}// end case
					default:
					{
						return;
						break;
					}// end default
				}// end switch
				if (m_nStatus == REQ_ERROR)
				{
					return;
				}
				if (m_nStatus == REQ_DONE)
				{
					_loc_3 = new DSocketEvent(DSocketEvent.SYNC);
					dispatchEvent(_loc_3);
					_loc_3 = null;
					reset();
				}
			}
			return;
		}

		private function sendcmd() : void
		{
			if (connected)
			{
				m_socket.writeBytes(m_packet, 0, m_packetlen + PACKET_HEADER_SIZE);
				m_socket.flush();
			}
			return;
		}

		private function parse_body(nLength:int) : Boolean
		{
			var _loc_2:int;
			var _loc_3:int;
			var _loc_4:uint;
			var _loc_5:int;
			_loc_2 = m_nBodyLen + PACKET_HEADER_SIZE - m_nreadPos - 4;
			_loc_3 = nLength - m_ndx;
			if (_loc_2 <= 0)
			{
				return true;
			}
			if (_loc_3 <= 0)
			{
				return false;
			}
			_loc_4 = _loc_3 < _loc_2 ? (_loc_3) : (_loc_2);
			_loc_5 = m_readpacket.length + _loc_4;
			if (_loc_5 > PACKET_BUFFER_SIZE)
			{
				return false;
			}
			m_socket.readBytes(m_readpacket, m_readpacket.position, _loc_4);
			m_nreadPos = m_nreadPos + _loc_4;
			m_ndx = m_ndx + _loc_4;
			if (m_nreadPos < m_nBodyLen + PACKET_HEADER_SIZE - 4)
			{
				m_readpacket.position = m_nreadPos;
				return false;
			}
			return true;
		}

		public function getsubver() : int
		{
			m_readpacket.position = 5;
			return m_readpacket.readByte();
		}
		
		private function errorFunWithDoNothing(err:IOErrorEvent) : void
		{
		}
		
		private function errorFun(err:IOErrorEvent) : void
		{
			close();
			if (!m_isProxyEnabled && m_proxyHost != "") 
			{
				m_isProxyEnabled = true;
				connect();
			}
			else
			{
				dispatchEvent(new DSocketEvent(DSocketEvent.ERROR));
			}
		}
		
		private function securityerrfunWithDoNothing(param1:SecurityErrorEvent) : void
		{
		}
		
		private function securityerrfun(param1:SecurityErrorEvent) : void
		{
			close();
			if (!m_isProxyEnabled && m_proxyHost != "") 
			{
				m_isProxyEnabled = true;
				connect();
			}
			else
			{
				dispatchEvent(new DSocketEvent(DSocketEvent.SECURITYERROR));
			}
		}

		public function readByte() : int
		{
			var nret:int = 0;
			try
			{
				m_readpacket.position = m_readbodypos;
				m_readbodypos = m_readbodypos + 1;
				nret = m_readpacket.readByte();
			}
			catch (err:Error)
			{
				trace(err.getStackTrace());
				throw err;
			}
			return nret;
		}

		public function readShort() : int
		{
			var nret:int = 0;
			try
			{			
				m_readpacket.position = m_readbodypos;
				m_readbodypos = m_readbodypos + 2;
				nret = m_readpacket.readShort();
			}
			catch (err:Error)
			{
				trace(err.getStackTrace());
				throw err;
			}
			return nret;
		}

		private function connectFun(param1:Event) : void
		{
			trace("............." + this.m_host + ":" + m_port  + " 连接成功!");
			//m_timer.stop();
			connected = true;
			clearEvent();
			if (m_isProxyEnabled)	//走HTTP代理
			{
				m_socket.writeMultiByte("CONNECT " + m_host + ":" + m_port + " HTTP/1.1\r\n\r\n", "gb2312");
				m_socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataChannel);
				m_socket.addEventListener(Event.CLOSE, closeFun);
				m_proxyChannelByteArray = [];
			}
			else					//走正常流程
			{
				m_socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataFun);
				m_socket.addEventListener(Event.CLOSE, closeFun);
				dispatchEvent(new DSocketEvent(DSocketEvent.CONNECT));
			}
		}
		
		private var m_proxyChannelByteArray:Array = new Array();
		//用于监测HTTP通道建立成功的信号
		private function socketDataChannel(e:ProgressEvent):void 
		{
			while (m_socket.bytesAvailable > 0)
			{
				var c:int = m_socket.readByte();
				//trace("//" + c);
				m_proxyChannelByteArray.push(c);
				var len:int = m_proxyChannelByteArray.length;
				if (len >= 4
					&& m_proxyChannelByteArray[len - 4] == 0x0D
					&& m_proxyChannelByteArray[len - 3] == 0x0A
					&& m_proxyChannelByteArray[len - 2] == 0x0D
					&& m_proxyChannelByteArray[len - 1] == 0x0A
				)
				{
					trace("///////////" + " proxy connect " + m_socket.bytesAvailable);
					m_socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataFun);
					m_socket.addEventListener(Event.CLOSE, closeFun);
					dispatchEvent(new DSocketEvent(DSocketEvent.CONNECT));
					if (m_socket.bytesAvailable > 0) socketDataFun(null);
					m_socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketDataChannel);
					return;
				}
			}
		}

		private function reset() : void
		{
			m_nStatus = REQ_REQUEST;
			m_nreadPos = 0;
			m_nBodyLen = 0;
			m_readpacket.position = 0;
			m_readpacket.length = 0;
			return;
		}

		public function readbegin() : void
		{
			m_readbodypos = PACKET_HEADER_SIZE;
			return;
		}

		private function read_header(param1:int) : Boolean
		{
			if (m_socket == null) return true;
			while (m_nreadPos < PACKET_HEADER_SIZE && m_ndx < param1)
			{
				m_socket.readBytes(m_readpacket, m_nreadPos, 1);
				m_nreadPos++;
				m_ndx++;
			}
			if (m_nreadPos < PACKET_HEADER_SIZE)
			{
				return false;
			}
			return true;
		}

		public function writeString(szValue:String) : void
		{
			var data:ByteArray;
			data = new ByteArray();
			data.writeMultiByte(szValue, "utf-8");
			
			m_packetbody.writeShort(data.length);
			m_packetlen = m_packetlen + 2;
			
			m_packetbody.writeBytes(data);
			m_packetlen = m_packetlen + (data.length);
			return;
		}

		public function writeUint(nValue:uint) : void
		{
			m_packetlen = m_packetlen + 4;
			m_packetbody.writeUnsignedInt(nValue);
			return;
		}

		public function writeShort(nValue:int) : void
		{
			m_packetlen = m_packetlen + 2;
			m_packetbody.writeShort(nValue);
			return;
		}

		public function readString() : String
		{
			var nret:String = "";
			try
			{	
				var len:uint;
				m_readpacket.position = m_readbodypos;
				m_readbodypos = m_readbodypos + 2;
				len = m_readpacket.readShort();
				m_readpacket.position = m_readbodypos;
				m_readbodypos = m_readbodypos + len;
				nret = m_readpacket.readMultiByte(len, "utf-8");
			}
			catch (err:Error)
			{
				trace(err.getStackTrace());
				throw err;
			}
			return nret;
		}

		//private var m_timer:Timer = null;
		
		public function connect(szHost:String = "", nIp:int = 0) : void
		{
			if (szHost != "" && nIp != 0)
			{
				m_host = szHost;
				m_port = nIp;
			}
			
			if (connected)
			{
				m_socket.close();
			}
			if (m_socket != null) 
			{
				clearEvent();
				m_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityerrfunWithDoNothing);
				m_socket.addEventListener(IOErrorEvent.IO_ERROR, errorFunWithDoNothing);
			}
			
			m_socket = new Socket();
			m_socket.endian = Endian.LITTLE_ENDIAN;
			
			if (m_isProxyEnabled)
			{
				trace("......connect through " + m_proxyHost + ":" + m_proxyPort);
				m_socket.connect(m_proxyHost, m_proxyPort);				
			}
			else
			{
				m_socket.connect(m_host, m_port);
			}
			reset();
			
			
			m_socket.addEventListener(IOErrorEvent.IO_ERROR, errorFun);
			m_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityerrfun);
			m_socket.addEventListener(Event.CONNECT, connectFun);
			return;
		}

		private function clearEvent() : void
		{
			if (m_socket != null)
			{
				if (m_socket.hasEventListener(IOErrorEvent.IO_ERROR))
				{
					m_socket.removeEventListener(IOErrorEvent.IO_ERROR, errorFun);
				}
				if (m_socket.hasEventListener(Event.CONNECT))
				{
					m_socket.removeEventListener(Event.CONNECT, connectFun);
				}
				if (m_socket.hasEventListener(ProgressEvent.SOCKET_DATA))
				{
					m_socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketDataFun);
				}
				if (m_socket.hasEventListener(Event.CLOSE))
				{
					m_socket.removeEventListener(Event.CLOSE, closeFun);
				}
				if (m_socket.hasEventListener(SecurityErrorEvent.SECURITY_ERROR))
				{
					m_socket.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityerrfun);
				}
			}
			return;
		}

		private function closeFun(param1:Event) : void
		{
			connected = false;
			dispatchEvent(new DSocketEvent(DSocketEvent.CLOSE));
			return;
		}

		public function readInt() : int
		{
			var nret:int = 0;
			try
			{
				m_readpacket.position = m_readbodypos;
				m_readbodypos = m_readbodypos + 4;
				nret = m_readpacket.readInt();
			}
			catch (err:Error)
			{
				trace(err.getStackTrace());
				throw err;
			}
			return nret;
		}

		public function writeEnd() : void
		{
			m_packet.writeInt(m_packetlen + 4);
			m_packet.writeBytes(m_packetbody, 0, m_packetlen);
			sendcmd();
			return;
		}

		public function close() : void
		{
			clearEvent();
			if (connected)
			{
				connected = false;
				m_socket.close();
				m_socket = null;
			}
			return;
		}
		
		public function get port():int { return m_port; }

		public function get ip():String { return m_host; }

		public function get isConnected():Boolean{ return connected; }

	}
}
